﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MacomberMapSystem.Common.Database;
using System.Threading;
using System.Xml;
using System.IO;
using MacomberMapSystem.Common.Processing;
using MacomberMapSystem.Common.Internals;
using MacomberMapSystem.Common.Serialization;
using WeifenLuo.WinFormsUI.Docking;
using System.Data.SQLite;
using MacomberMapSystem.Common.CIM.Direct;
using MacomberMapSystem.Common.User_Interfaces.One_Lines;

namespace MacomberMapSystem.Common.User_Interfaces.Database
{
    /// <summary>
    /// (C) 2013, Michael E. Legatt, Ph.D., Electric Reliability Council of Texas, Inc.
    /// This form handles a direct comparison between two models
    /// </summary>
    public partial class frmComparer : DockContent
    {
        #region Variable declarations
        /// <summary>Our difference report</summary>
        public CIM_DifferenceReport Comparer;

        /// <summary>Our old model</summary>
        public MM_Database_Model OldModel = null;

        /// <summary>Our new model</summary>
        public MM_Database_Model NewModel = null;      

        /// <summary>The repository associated with our comparer</summary>
        public MM_Repository Repository;
        #endregion

        #region Initialization
        /// <summary>
        /// Initialize a new comparison form
        /// </summary>
        public frmComparer(MM_Repository Repository)
        {
            InitializeComponent();
            this.Repository = Repository;
            this.txtEmail.Text = Environment.UserName + "@ercot.com";
       
        }
        #endregion

        /// <summary>
        /// Offer the user the model
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SelectModel(object sender, EventArgs e)
        {
            bool OldModel = sender.Equals(btnOpenOldModel);
            bool UseSelector = (sender as Control).Name.Contains("Select");
            String SelectedFile = null;

            if (UseSelector)
                using (frmSelectModel SelModel = new frmSelectModel(Repository))
                {
                    if (SelModel.ShowDialog() == DialogResult.OK)
                        SelectedFile = Path.Combine(SelModel.SelectedModel.ModelFullPath, SelModel.SelectedModel.NameWithoutExtension + (SelModel.DirectMode ? ".cimindex" : ".sqlite"));
                }
            else
                using (OpenFileDialog oFd = new OpenFileDialog())
                {
                    oFd.Title = "Select " + (OldModel ? "old" : "new") + " network model";
                    oFd.Filter = "CIM Models (*.xml;*.CimIndex;*.SQLite)|*.xml;*.CimIndex;*.SQLite|CIM raw XML files (*.xml)|*.xml|CIM old-style indexes (*.CimIndex)|*.CimIndex|CIM new indexes (*.SQLite)|*.SQLite";
                    oFd.SupportMultiDottedExtensions = true;
                    oFd.ValidateNames = true;
                    oFd.CheckFileExists = true;
                    oFd.CheckPathExists = true;
                    oFd.AutoUpgradeEnabled = true;
                    if (oFd.ShowDialog() == DialogResult.OK)
                        SelectedFile = oFd.FileName;
                }

            if (String.IsNullOrEmpty(SelectedFile))
                return;

            (OldModel ? btnOpenOldModel : btnSelectOldModel).Tag = SelectedFile;
            (OldModel ? lblOldModelName : lblNewModelName).Text = Path.GetFileNameWithoutExtension(SelectedFile);
            MM_Database_Model Model;
            if (Path.GetExtension(SelectedFile).Equals(".sqlite", StringComparison.CurrentCultureIgnoreCase))
            {
                Model = new MM_Database_Model();
                Model.Repository = Repository;
                MM_Database_Connector dConn = new MM_Database_Connector(Model.Repository);
                Model.Name = Path.GetFileNameWithoutExtension(SelectedFile);
                Model.FullPath = Path.GetDirectoryName(SelectedFile);
                Model.ModelLocation = MM_Database_Model.enumModelLocations.InPrimary;
                Model.sConn = new SQLiteConnection("Data Source=" + SelectedFile);
                Model.sConn.Open();
                dConn.ModelIndexPath = Path.GetDirectoryName(SelectedFile);
                Model.Db = dConn;
                Model.InitiateLoading();
            }
            else
                Model = new CIM_DirectModel(SelectedFile, true, null);

            if (OldModel)
                this.OldModel = Model;
            else
                this.NewModel = Model;

        }



        /// <summary>
        /// Allow the user to select emails
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOutputXml_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog sFd = new SaveFileDialog())
            {
                sFd.Title = "Select output for CIM comparison";
                sFd.Filter = "XML output (*.xml)|*.xml";
                sFd.CheckPathExists = true;
                sFd.AddExtension = true;
                sFd.AutoUpgradeEnabled = true;
                sFd.OverwritePrompt = true;
                if (sFd.ShowDialog() == DialogResult.OK)
                {
                    btnOutputXml.Tag = sFd.FileName;
                    lblOutputFileName.Text = Path.GetFileNameWithoutExtension(sFd.FileName);
                }
            }
        }


        /// <summary>
        /// Process the comparison
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnProcess_Click(object sender, EventArgs e)
        {
            //First, wait for our models to be loaded
            if (OldModel == null || NewModel == null)
                MessageBox.Show("Both new and old models must first be loaded.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            else if (OldModel.State != MM_Database_Model.enumModelState.Completed || NewModel.State != MM_Database_Model.enumModelState.Completed)
            {
                MessageBox.Show("Models are still loading. The application will wait until they're done, then run the comparison");
                ThreadPool.QueueUserWorkItem(new WaitCallback(CompareModel));
            }
            else
                ThreadPool.QueueUserWorkItem(new WaitCallback(CompareModel));
        }

        /// <summary>
        /// Perform the actual model comparison in its own thread
        /// </summary>
        /// <param name="state"></param>
        private void CompareModel(Object state)
        {
            while (OldModel.State != MM_Database_Model.enumModelState.Completed || NewModel.State != MM_Database_Model.enumModelState.Completed)
                Thread.Sleep(1000);


            Dictionary<String, Object> NamedParameters = new Dictionary<string, object>();
            NamedParameters.Add("Model", NewModel);
            NamedParameters.Add("ModelName", NewModel.Name);
            NamedParameters.Add("OldModel", OldModel);
            NamedParameters.Add("OldModelName", OldModel.Name);

            XmlDocument ConfigDoc = new XmlDocument();
            ConfigDoc.LoadXml("<Processor Description=\"Produce Difference Report\" SMTPServer=\"[SMTPServer]\" Module=\"CIM_DifferenceReport\" HasProperties=\"true\" Step=\"1\" Message=\"The comparison process has completed, comparing new model [ModelName] against [OldModelName].\"/>");
            Comparer = new CIM_DifferenceReport(ConfigDoc.DocumentElement, NamedParameters, null);
            Comparer.TargetFolder = Path.GetDirectoryName(btnOutputXml.Tag.ToString());
            Comparer.TargetFileName = Path.GetFileName(btnOutputXml.Tag.ToString());
            Comparer.MailRecipients = new MM_String_Collection(txtEmail.Text.Split(';', ','));
            Comparer.ProgressCheck.Location = new Point(10, btnProcess.Bottom + 10);
            Comparer.ProgressCheck.Width = DisplayRectangle.Width - 20;
            Comparer.ProgressCheck.Checked = true;
            Comparer.HasProperties = false;
            Comparer.CompareContingencies = chkCompareContingencies.Checked;
            Comparer.RunComparisonTool = chkCompareContingencies.Checked;
            AddControl(Comparer.ProgressCheck);
            Comparer.Repository = Repository;
            Comparer.InitiateProcessInPooledThread(Repository.Db);
        }

        private delegate void SafeAddControl(Control CtlToAdd);
        /// <summary>
        /// Add a control to the system
        /// </summary>
        /// <param name="CtlToAdd"></param>
        private void AddControl(Control CtlToAdd)
        {
            if (InvokeRequired)
                Invoke(new SafeAddControl(AddControl), CtlToAdd);
            else
                Controls.Add(CtlToAdd);
        }


        /// <summary>
        /// Update the status of our model loading
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateModelLoadProgress(object sender, PaintEventArgs e)
        {
            MM_Database_Model Model = (sender.Equals(lblOldModelName) ? this.OldModel : this.NewModel);
            if (Model == null)
                return;

            //If our model is loading, update our background
            if (Model.State != MM_Database_Model.enumModelState.Completed && Model.State != MM_Database_Model.enumModelState.Unopened)
            {
                //Blank our background, draw our progress bar, and percentages
                Label lblModelName = sender as Label;
                e.Graphics.FillRectangle(SystemBrushes.Control, e.ClipRectangle);
                RectangleF DrawBounds = new RectangleF(0, 0, lblModelName.Width * Model.StepProgress, lblModelName.Height);
                Rectangle LabelBounds = new Rectangle(Point.Empty, lblModelName.Size);
                e.Graphics.FillRectangle(Brushes.LightBlue, DrawBounds);
                int MarqueeTime = 5;
                if (Model.StepProgress > 0)
                {
                    TimeSpan RunTime = DateTime.Now - Model.StartTime;
                    Double Rem = (RunTime.TotalSeconds * (1 - Model.StepProgress)) / Model.StepProgress;
                    String RemStr = "Infinite";
                    if (Rem < TimeSpan.MaxValue.TotalSeconds)
                    {
                        TimeSpan RemainingTime = TimeSpan.FromSeconds(Rem);
                        RemStr = RemainingTime.TotalHours.ToString("00") + ":" + RemainingTime.Minutes.ToString("00") + ":" + RemainingTime.Seconds.ToString("00");
                    }
                    if (DateTime.Now.Second % (MarqueeTime * 2) < MarqueeTime)
                        e.Graphics.DrawString(lblModelName.Text, lblModelName.Font, Brushes.Black, LabelBounds, MM_OneLine_Element.CenterFormat);
                    else
                        e.Graphics.DrawString(Model.State.ToString().Replace('_', ' ') + " " + Model.StepProgress.ToString("0.0%") + " | " + RemStr, lblModelName.Font, Brushes.Black, LabelBounds, MM_OneLine_Element.CenterFormat);
                }
                else if (DateTime.Now.Second % (MarqueeTime * 2) < MarqueeTime)
                    e.Graphics.DrawString(lblModelName.Text, lblModelName.Font, Brushes.Black, LabelBounds, MM_OneLine_Element.CenterFormat);
                else
                    e.Graphics.DrawString(Model.State.ToString().Replace('_', ' ') + " " + Model.StepProgress.ToString("0.0%"), lblModelName.Font, Brushes.Black, LabelBounds, MM_OneLine_Element.CenterFormat);
            }

        }

        /// <summary>
        /// When our timer ticks, refresh our load status pages
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tmrUpdate_Tick(object sender, EventArgs e)
        {
            if (Comparer != null)
                Comparer.ProgressCheck.Refresh();
            lblOldModelName.Refresh();
            lblNewModelName.Refresh();
        }

    }
}